package com.liuqi.knet.remoting.transport;

import com.liuqi.knet.remoting.Channel;
import com.liuqi.knet.remoting.ChannelHandler;
import com.liuqi.knet.remoting.KNetConfig;
import com.liuqi.knet.remoting.RemotingException;
import com.liuqi.knet.remoting.exchange.Request;
import com.liuqi.knet.remoting.exchange.ResponseFuture;
import com.liuqi.knet.remoting.exchange.support.DefaultResponseFuture;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author liuqi
 * @date 2025/2/8 16:38
 **/
public abstract class AbstractChannel extends AbstractEndpoint implements Channel {

    private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();

    public AbstractChannel(ChannelHandler handler, KNetConfig netConfig) {
        super(handler, netConfig);
    }

    @Override
    public ResponseFuture request(Object request) throws RemotingException {
        return request(request, getTimeout());
    }

    @Override
    public ResponseFuture request(Object request, int timeout) throws RemotingException {
        Request req;
        if (request instanceof Request) {
            req = (Request) request;
        } else {
            req = new Request();
            req.setData(request);
        }

        ResponseFuture future = null;
        if (req.isTwoWay()) {
            // 是需要服务端响应的
            future = new DefaultResponseFuture(this, req, timeout);
        }
        try {
            send(req, isSent());    // send message
        } catch (RemotingException e) {
            if (future != null) future.cancel();
            throw e;
        }
        return future;
    }

    @Override
    public void send(Object message, boolean sent) throws RemotingException {
        if (isClosed()) {
            throw new RemotingException("无法发送消息: message: "
                    + (message == null ? "" : message.getClass().getName()) + ":" + message
                    + ", 原因: 通道已经关闭. channel: " + getLocalAddress() + " -> " + getRemoteAddress());
        }
    }

    @Override
    public boolean hasAttribute(String key) {
        return attributes.containsKey(key);
    }

    @Override
    public Object getAttribute(String key) {
        return attributes.get(key);
    }

    @Override
    public void setAttribute(String key, Object value) {
        if (value == null) {
            attributes.remove(key);
        } else {
            attributes.put(key, value);
        }
    }

    @Override
    public void removeAttribute(String key) {
        attributes.remove(key);
    }

    @Override
    public String toString() {
        return getLocalAddress() + " -> " + getRemoteAddress();
    }

}
